const fs = require('fs').promises;
const path = require('path');
const sharp = require('sharp');

/**
 * 获取所有 tiles 的 x 和 y 范围
 * @param {string} zoomDir - zoom 目录路径
 * @returns {Promise<{ minX: number, maxX: number, minY: number, maxY: number }>} - 返回 x 和 y 的范围
 */
async function getTileRange(zoomDir) {
    const xDirs = await fs.readdir(zoomDir);
    const xValues = xDirs
        .map((dir) => parseInt(dir, 10))
        .filter((x) => !isNaN(x))
        .sort((a, b) => a - b);

    if (xValues.length === 0) {
        throw new Error('未找到任何有效的 x 目录');
    }

    let minX = Infinity;
    let maxX = -Infinity;
    let minY = Infinity;
    let maxY = -Infinity;

    for (const x of xValues) {
        const yDir = path.join(zoomDir, x.toString());
        const yFiles = await fs.readdir(yDir);
        const yValues = yFiles
            .filter((file) => file.endsWith('.png'))
            .map((file) => parseInt(file.split('.')[0], 10))
            .filter((y) => !isNaN(y))
            .sort((a, b) => a - b);

        if (yValues.length === 0) {
            continue;
        }

        // 更新 x 范围
        minX = Math.min(minX, x);
        maxX = Math.max(maxX, x);

        // 更新 y 范围
        minY = Math.min(minY, yValues[0]);
        maxY = Math.max(maxY, yValues[yValues.length - 1]);
    }

    return { minX, maxX, minY, maxY };
}

/**
 * 在 tile 上绘制边框和 x/y 值
 * @param {Buffer} tileBuffer - tile 的 Buffer
 * @param {number} x - tile 的 x 值
 * @param {number} y - tile 的 y 值
 * @param {number} tileWidth - 瓦片宽度
 * @param {number} tileHeight - 瓦片高度
 * @param {boolean} showText - 是否显示文字
 * @returns {Promise<Buffer>} - 返回绘制后的 tile Buffer
 */
async function drawDebugInfo(tileBuffer, x, y, tileWidth, tileHeight, showText) {
    const tile = sharp(tileBuffer);

    // 绘制边框
    const borderWidth = 2;
    const borderColor = 'red';
    const borderedTile = await tile
        .extend({
            top: borderWidth,
            bottom: borderWidth,
            left: borderWidth,
            right: borderWidth,
            background: borderColor
        })
        .toBuffer();

    // 如果不显示文字，直接返回带边框的 tile
    if (!showText) {
        return borderedTile;
    }

    // 绘制 x/y 值
    const text = `${x}-${y}`;
    const textColor = 'white';
    const textBackground = 'rgba(0, 0, 0, 0.5)';
    const textBuffer = await sharp({
        create: {
            width: tileWidth,
            height: 25,
            channels: 4,
            background: textBackground
        }
    })
        .composite([
            {
                input: Buffer.from(`<svg><text x="0" y="15" font-size="22" fill="${textColor}">${text}</text></svg>`),
                top: 0,
                left: 0
            }
        ])
        .png()
        .toBuffer();

    // 将文本叠加到 tile 上
    const debugTile = await sharp(borderedTile)
        .composite([
            {
                input: textBuffer,
                top: borderWidth,
                left: borderWidth
            }
        ])
        .png()
        .toBuffer();

    return debugTile;
}

/**
 * 拼接 tiles 成一张大图
 * @param {string} inputDir - 输入目录
 * @param {string} outputPath - 输出路径
 * @param {number} zoom - zoom 级别
 * @param {boolean} debug - 是否启用调试模式
 * @param {boolean} showText - 是否显示文字
 */
async function mergeTiles(inputDir, outputPath, zoom, debug = false, showText = false) {
    try {
        const zoomDir = path.join(inputDir, `zoom${zoom}`);

        // 检查 zoom 目录是否存在
        try {
            await fs.access(zoomDir);
        } catch (error) {
            console.error(`未找到 zoom${zoom} 目录: ${zoomDir}`);
            return;
        }

        // 获取所有 tiles 的 x 和 y 范围
        const { minX, maxX, minY, maxY } = await getTileRange(zoomDir);

        // 初始化瓦片尺寸
        let tileWidth = null;
        let tileHeight = null;

        // 遍历所有 x 和 y 目录，获取第一个瓦片的尺寸
        for (let x = minX; x <= maxX; x++) {
            const yDir = path.join(zoomDir, x.toString());

            // 检查 y 目录是否存在
            try {
                await fs.access(yDir);
            } catch (error) {
                console.warn(`未找到 y 目录: ${yDir}，跳过`);
                continue;
            }

            // 读取所有 y 文件
            const yFiles = await fs.readdir(yDir);
            const yValues = yFiles
                .filter((file) => file.endsWith('.png'))
                .map((file) => parseInt(file.split('.')[0], 10))
                .filter((y) => !isNaN(y))
                .sort((a, b) => a - b);

            if (yValues.length > 0) {
                const tilePath = path.join(yDir, `${yValues[0]}.png`);
                const tileMetadata = await sharp(tilePath).metadata();
                tileWidth = tileMetadata.width;
                tileHeight = tileMetadata.height;
                break;
            }
        }

        if (tileWidth === null || tileHeight === null) {
            throw new Error('未找到有效的瓦片');
        }

        // 计算大图的尺寸
        const finalWidth = (maxX - minX + 1) * tileWidth;
        const finalHeight = (maxY - minY + 1) * tileHeight;

        // 创建空白大图
        const finalImage = sharp({
            create: {
                width: finalWidth,
                height: finalHeight,
                channels: 4,
                background: { r: 0, g: 0, b: 0, alpha: 0 }
            }
        });

        // 遍历所有 x 和 y 目录，将 tiles 放置到正确的位置
        const composites = [];
        for (let x = minX; x <= maxX; x++) {
            const yDir = path.join(zoomDir, x.toString());

            // 检查 y 目录是否存在
            try {
                await fs.access(yDir);
            } catch (error) {
                console.warn(`未找到 y 目录: ${yDir}，跳过`);
                continue;
            }

            // 读取所有 y 文件
            const yFiles = await fs.readdir(yDir);
            const yValues = yFiles
                .filter((file) => file.endsWith('.png'))
                .map((file) => parseInt(file.split('.')[0], 10))
                .filter((y) => !isNaN(y))
                .sort((a, b) => a - b);

            for (const y of yValues) {
                const tilePath = path.join(yDir, `${y}.png`);

                // 检查图片尺寸是否正确
                const tileMetadata = await sharp(tilePath).metadata();
                if (tileMetadata.width !== tileWidth || tileMetadata.height !== tileHeight) {
                    console.warn(`图片尺寸不正确，跳过: ${tilePath}，metadata = `, tileMetadata);
                    continue;
                }

                // 计算 tile 在大图中的位置
                const left = (x - minX) * tileWidth;
                const top = (y - minY) * tileHeight;

                let tileBuffer = await sharp(tilePath).toBuffer();

                // 如果启用调试模式，绘制边框和 x/y 值
                if (debug) {
                    tileBuffer = await drawDebugInfo(tileBuffer, x, y, tileWidth, tileHeight, showText);
                }

                composites.push({
                    input: tileBuffer,
                    left,
                    top
                });
            }
        }

        // 拼接最终大图
        await finalImage.composite(composites).png().toFile(outputPath);

        console.log(`大图已保存到: ${outputPath}`);
    } catch (error) {
        console.error('拼接图片时出错:', error);
    }
}

// 示例调用
(async () => {
    const rootDir = '/Users/laotie/Library/Application Support/labelmap/tiles';
    const inputDir = rootDir;
    const outputPath = path.join(rootDir, 'merged_image.png');
    const zoom = 19;

    await mergeTiles(inputDir, outputPath, zoom, true, true);
})();